// pipelineDrawer.js

export class PipelineDrawer {
    constructor(canvas, pipelineConfig) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.pipelineConfig = pipelineConfig;
        this.loaded = false;
        this.scale = 1;
        this.offsetX = 0;
        this.offsetY = 0;
        this.isDragging = false;
        this.lastX = 0;
        this.lastY = 0;

        // 默认视角参数
        this.defaultScale = 1;
        this.defaultOffsetX = 0;
        this.defaultOffsetY = 0;

        // 节点样式
        this.nodeWidth = 120;
        this.nodeHeight = 60;
        this.nodePadding = 40;
        this.levelHeight = 100;

        // 输入输出节点样式
        this.ioNodeRadius = 20; // 输入输出节点的半径
        this.ioNodeColor = '#a1c4fd'; // 输入输出节点的颜色

        // 外包矩形框样式
        this.boxMargin = 20; // 外包矩形框的 margin
        this.boxStrokeStyle = '#409eff'; // 外包矩形框的颜色
        this.boxLineWidth = 2; // 外包矩形框的线宽
        this.boxDash = [5, 5]; // 外包矩形框的虚线样式

        // 输入输出节点距离外包矩形边界的固定值
        this.ioNodeMargin = 80;

        // 初始化事件监听
        this.initEventListeners();

        // 初始化默认视角
        this.resetView();
    }

    // 初始化事件监听
    initEventListeners() {
        // 取消右键默认菜单
        this.canvas.addEventListener('contextmenu', (e) => e.preventDefault());

        // 鼠标按下事件
        this.canvas.addEventListener('mousedown', (e) => {
            if (!this.loaded) {
                return;
            }
            if (e.button === 2) { // 右键
                this.isDragging = true;
                this.lastX = e.offsetX;
                this.lastY = e.offsetY;
            } else if (e.button === 0) { // 左键
                // 处理节点点击逻辑
                this.handleNodeClick(e);
            }
        });

        // 鼠标移动事件
        this.canvas.addEventListener('mousemove', (e) => {
            if (this.isDragging) {
                const dx = e.offsetX - this.lastX;
                const dy = e.offsetY - this.lastY;
                this.offsetX += dx;
                this.offsetY += dy;
                this.lastX = e.offsetX;
                this.lastY = e.offsetY;
                this.draw();
            }
        });

        // 鼠标松开事件
        this.canvas.addEventListener('mouseup', () => {
            this.isDragging = false;
        });

        // 鼠标滚轮事件
        this.canvas.addEventListener('wheel', (e) => {
            if (!this.loaded) {
                return;
            }

            e.preventDefault();
            const scaleFactor = 0.1;
            const mouseX = (e.offsetX - this.offsetX) / this.scale;
            const mouseY = (e.offsetY - this.offsetY) / this.scale;

            if (e.deltaY < 0) {
                this.scale *= 1 + scaleFactor;
            } else {
                this.scale /= 1 + scaleFactor;
            }

            this.offsetX = e.offsetX - mouseX * this.scale;
            this.offsetY = e.offsetY - mouseY * this.scale;

            this.draw();
        });
    }

    // 恢复默认视角
    resetView() {
        const { boxX, boxY, boxWidth, boxHeight } = this.calculateLayout();

        // 计算默认偏移量，使 Pipeline 居中
        this.offsetX = (this.canvas.width / 2 - (boxX + boxWidth / 2) * this.scale);
        this.offsetY = (this.canvas.height / 2 - (boxY + boxHeight / 2) * this.scale);

        this.draw();
    }

    // 检查节点名称是否重复
    checkDuplicateNodeNames(nodeList) {
        const nameSet = new Set();
        const duplicates = new Set();

        const checkNode = (node) => {
            if (node.name) {
                if (nameSet.has(node.name)) {
                    duplicates.add(node.name);
                } else {
                    nameSet.add(node.name);
                }
            }
            if (node.nodes) {
                node.nodes.forEach(checkNode);
            }
        };

        nodeList.forEach(checkNode);
        return Array.from(duplicates);
    }

    // 绘制 Pipeline
    draw() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.ctx.save();
        this.ctx.translate(this.offsetX, this.offsetY);
        this.ctx.scale(this.scale, this.scale);

        // 检查节点名称是否重复
        const duplicates = this.checkDuplicateNodeNames(this.pipelineConfig.nodes);
        if (duplicates.length > 0) {
            console.log(`duplicates = `, duplicates, ' width = ', this.canvas.width / 2, ' height = ', this.canvas.height / 2);

            // 如果有重复名称，绘制提示信息并停止绘制
            this.ctx.resetTransform();
            this.ctx.font = '20px Arial';
            this.ctx.fillStyle = 'red';
            this.ctx.textAlign = 'center';
            this.ctx.fillText(`Duplicate Node: ${duplicates.join(', ')}`, this.canvas.width / 2, this.canvas.height / 2);
            this.ctx.restore();

            // 设置加载失败的标志位
            this.loaded = false;
            return;
        }

        // 计算所有节点的位置
        const { nodes, inputX, inputY, outputX, outputY, boxX, boxY, boxWidth, boxHeight } = this.calculateLayout();

        // 绘制外包矩形框
        this.drawPipelineBox(boxX, boxY, boxWidth, boxHeight);

        // 绘制输入输出节点
        this.drawInputOutput(inputX, inputY, outputX, outputY);

        // 绘制节点
        this.drawNodes(nodes);

        // 绘制连接线和箭头
        this.drawConnections(nodes);

        this.ctx.restore();

        this.loaded = true;
    }

    // 计算布局
    processNodes(nodeList, startX, startY, parentType, config) {
        const { nodeWidth, nodeHeight, nodePadding } = config;
        const nodes = [];
        let maxX = startX;
        let minY = Infinity;
        let maxY = -Infinity;

        nodeList.forEach((node, index) => {
            const nodeX = parentType === 'parallel' ? startX : startX + index * (nodeWidth + nodePadding);
            const nodeY = parentType === 'parallel' ? startY + index * (nodeHeight + nodePadding) : startY;

            if (node.type === 'node') {
                // 普通节点
                nodes.push({ ...node, x: nodeX, y: nodeY });
                maxX = Math.max(maxX, nodeX + nodeWidth);
                minY = Math.min(minY, nodeY);
                maxY = Math.max(maxY, nodeY + nodeHeight);
            } else if (node.type === 'parallel' || node.type === 'serial') {
                // 处理节点组，但不绘制外接矩形
                const { nodes: childNodes, minY: childMinY, maxY: childMaxY } = this.processNodes(
                    node.nodes,
                    nodeX,
                    nodeY,
                    node.type,
                    config
                );

                // 更新当前组的范围
                minY = Math.min(minY, childMinY);
                maxY = Math.max(maxY, childMaxY);
                maxX = Math.max(maxX, nodeX + nodeWidth);

                // 将子节点加入 nodes
                nodes.push(...childNodes);
            }
        });

        return { nodes, minY, maxY, maxX };
    }

    calculateLayout() {
        const inputX = this.ioNodeMargin;
        const config = {
            nodeWidth: this.nodeWidth,
            nodeHeight: this.nodeHeight,
            nodePadding: this.nodePadding,
            boxMargin: this.boxMargin,
        };

        // 调用 processNodes 计算节点布局
        const { nodes, minY, maxY, maxX } = this.processNodes(
            this.pipelineConfig.nodes,
            inputX + this.ioNodeMargin,
            0,
            'serial',
            config
        );

        // 计算外包矩形
        const boxX = inputX + this.ioNodeMargin - this.boxMargin;
        const boxY = minY - this.boxMargin;
        const boxWidth = maxX - (inputX + this.ioNodeMargin) + 2 * this.boxMargin;
        const boxHeight = maxY - minY + 2 * this.boxMargin;

        // 计算输入输出节点位置
        const centerY = boxY + boxHeight / 2;
        const inputY = centerY;
        const outputX = maxX + this.ioNodeMargin;
        const outputY = centerY;

        return { nodes, inputX, inputY, outputX, outputY, boxX, boxY, boxWidth, boxHeight };
    }

    // 绘制输入输出节点
    drawInputOutput(inputX, inputY, outputX, outputY) {
        // 绘制输入节点
        this.drawIONode(inputX, inputY, 'Input');

        // 绘制输出节点
        this.drawIONode(outputX, outputY, 'Output');

        // 绘制从输入节点到外包矩形左边界的连接线
        this.drawBezierConnection(
            inputX + this.ioNodeRadius, inputY,
            inputX + this.ioNodeMargin - this.boxMargin, inputY
        );

        // 绘制从外包矩形右边界到输出节点的连接线
        this.drawBezierConnection(
            inputX + this.ioNodeMargin + (this.nodeWidth + this.nodePadding) * (this.pipelineConfig.nodes.length - 1) + this.nodeWidth + this.boxMargin, inputY,
            outputX - this.ioNodeRadius, outputY
        );
    }

    // 绘制外包矩形框
    drawPipelineBox(boxX, boxY, boxWidth, boxHeight) {
        this.ctx.strokeStyle = this.boxStrokeStyle;
        this.ctx.lineWidth = this.boxLineWidth;
        this.ctx.setLineDash(this.boxDash);
        this.ctx.strokeRect(boxX, boxY, boxWidth, boxHeight);
        this.ctx.setLineDash([]); // 重置虚线样式
    }

    // 根据节点名称查找节点
    findNodeByName(nodeList, name) {
        for (const node of nodeList) {
            if (node.name === name) {
                return node;
            }
            if (node.type === 'parallel' || node.type === 'serial') {
                const found = this.findNodeByName(node.nodes, name);
                if (found) return found;
            }
        }
        return null;
    }

    // 绘制节点
    drawNodes(nodes) {
        nodes.forEach((node) => {
            const { x, y, name, worker, type } = node;

            if (type === 'node') {
                // 绘制普通节点
                this.drawNode(x, y, name, worker, '#e6f7ff');
            } else if (type === 'group') {
                // 绘制节点组的外包矩形框
                this.drawPipelineBox(x, y, node.width, node.height);
            }
        });
    }

    // 绘制连接线和箭头
    // 递归解析节点连接关系
    parseNodeConnections(node) {
        if (node.type === 'node') {
            // 普通节点，直接返回自身作为入口和出口
            return {
                entry: [node],
                exit: [node],
            };
        } else if (node.type === 'serial') {
            // serial 类型节点：递归处理子节点，串联入口和出口
            let entry = [];
            let exit = [];

            node.nodes.forEach((childNode, index) => {
                const { entry: childEntry, exit: childExit } = this.parseNodeConnections(childNode);

                if (index === 0) {
                    // 第一个子节点的入口是 serial 节点的入口
                    entry = childEntry;
                } else {
                    // 连接前一个子节点的出口和当前子节点的入口
                    exit.forEach((exitNode) => {
                        childEntry.forEach((entryNode) => {
                            this.connections.push({ from: exitNode.name, to: entryNode.name });
                        });
                    });
                }

                if (index === node.nodes.length - 1) {
                    // 最后一个子节点的出口是 serial 节点的出口
                    exit = childExit;
                } else {
                    // 更新当前子节点的出口
                    exit = childExit;
                }
            });

            return { entry, exit };
        } else if (node.type === 'parallel') {
            // parallel 类型节点：递归处理子节点，合并入口和出口
            const entry = [];
            const exit = [];

            node.nodes.forEach((childNode) => {
                const { entry: childEntry, exit: childExit } = this.parseNodeConnections(childNode);
                entry.push(...childEntry);
                exit.push(...childExit);
            });

            return { entry, exit };
        }

        // 默认返回空
        return { entry: [], exit: [] };
    }

    // 绘制连接线和箭头
    drawConnections(nodes) {
        // 动态解析节点之间的连接关系
        this.connections = [];

        // 递归解析节点关联关系
        this.parseNodeConnections({ type: 'serial', nodes: this.pipelineConfig.nodes });

        // 根据解析的连接关系绘制箭头
        this.connections.forEach((conn) => {
            const fromNode = this.findNodeByName(nodes, conn.from);
            const toNode = this.findNodeByName(nodes, conn.to);

            if (fromNode && toNode) {
                this.drawBezierConnection(
                    fromNode.x + this.nodeWidth, fromNode.y + this.nodeHeight / 2,
                    toNode.x, toNode.y + this.nodeHeight / 2
                );
            }
        });
    }

    // 绘制单个节点
    drawNode(x, y, name, worker, color) {
        // 绘制节点背景
        this.ctx.fillStyle = color;
        this.ctx.strokeStyle = '#409eff';
        this.ctx.lineWidth = 2;
        this.ctx.beginPath();
        this.ctx.roundRect(x, y, this.nodeWidth, this.nodeHeight, 8);
        this.ctx.fill();
        this.ctx.stroke();

        // 绘制节点名称
        this.ctx.fillStyle = '#000';
        this.ctx.font = '12px Arial';
        this.ctx.textAlign = 'center';

        // 如果长度超过10个字符，则前后各保留4个字符，中间用...补全
        worker = worker.replace(/worker$/i, '');
        if (worker.length > 15) {
            worker = worker.substring(0, 4) + '...' + worker.substring(worker.length - 4);
        }
        this.ctx.fillText(worker, x + this.nodeWidth / 2, y + 25);

        // 绘制 Worker 信息
        this.ctx.font = '12px Arial';
        this.ctx.fillStyle = '#666';
        this.ctx.fillText(name, x + this.nodeWidth / 2, y + 45);
    }

    // 绘制输入输出节点（圆形）
    drawIONode(x, y, label) {
        this.ctx.fillStyle = this.ioNodeColor;
        this.ctx.strokeStyle = '#409eff';
        this.ctx.lineWidth = 2;
        this.ctx.beginPath();
        this.ctx.arc(x, y, this.ioNodeRadius, 0, Math.PI * 2);
        this.ctx.fill();
        this.ctx.stroke();

        // 绘制标签
        this.ctx.fillStyle = '#000';
        this.ctx.font = '12px Arial';
        this.ctx.textAlign = 'center';
        this.ctx.fillText(label, x, y + 5);
    }

    // 绘制贝塞尔曲线连接线
    // 绘制贝塞尔曲线连接线
    drawBezierConnection(startX, startY, endX, endY) {
        const controlX1 = startX + (endX - startX) * 0.5;
        const controlY1 = startY;
        const controlX2 = startX + (endX - startX) * 0.5;
        const controlY2 = endY;

        // 绘制曲线
        this.ctx.strokeStyle = '#409eff';
        this.ctx.lineWidth = 2;
        this.ctx.beginPath();
        this.ctx.moveTo(startX, startY);
        this.ctx.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY);
        this.ctx.stroke();

        // 绘制水平箭头
        const arrowSize = 10; // 箭头大小
        const arrowDirection = endX > startX ? 1 : -1; // 箭头方向：1 表示向右，-1 表示向左

        // 箭头的位置
        const arrowX = endX; // 箭头起点与连接线终点对齐
        const arrowY = endY;

        // 绘制箭头
        this.ctx.beginPath();
        this.ctx.moveTo(arrowX, arrowY);
        this.ctx.lineTo(arrowX - arrowDirection * arrowSize, arrowY - arrowSize / 2);
        this.ctx.lineTo(arrowX - arrowDirection * arrowSize, arrowY + arrowSize / 2);
        this.ctx.closePath();
        this.ctx.fillStyle = '#409eff';
        this.ctx.fill();
    }

    // 处理节点点击逻辑
    handleNodeClick(event) {
        if (!this.loaded) {
            return;
        }

        const mouseX = (event.offsetX - this.offsetX) / this.scale;
        const mouseY = (event.offsetY - this.offsetY) / this.scale;

        // 遍历所有节点，检查是否点击了某个节点
        const { nodes, inputX, inputY, outputX, outputY } = this.calculateLayout();

        // 检查是否点击了输入节点
        const inputNodeDistance = (mouseX - inputX) ** 2 + (mouseY - inputY) ** 2;
        if (inputNodeDistance <= this.ioNodeRadius ** 2) {
            console.log('Clicked Input Node');
            return;
        }

        // 检查是否点击了输出节点
        const outputNodeDistance = (mouseX - outputX) ** 2 + (mouseY - outputY) ** 2;
        if (outputNodeDistance <= this.ioNodeRadius ** 2) {
            console.log('Clicked Output Node');
            return;
        }

        const clickedNode = nodes.find(node => {
            return mouseX >= node.x && mouseX <= node.x + this.nodeWidth &&
                mouseY >= node.y && mouseY <= node.y + this.nodeHeight;
        });

        if (clickedNode) {
            console.log('Clicked Node:', clickedNode);
            // 在这里处理节点点击逻辑
        }
    }
}